package org.dezenho.server.SO;

import java.rmi.Naming;
import java.rmi.RMISecurityManager;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import org.dezenho.common.RMI.ClientInterface;
import org.dezenho.server.RMI.ServerFacade;
import org.dezenho.server.RN.Jogador;
import org.dezenho.server.RN.Partida;
import org.dezenho.server.RN.SalaEspera;

/**
 * Classe SO responsavel pelo controle das conexes, inicializaca
 * do servidor e acesso as partidas e sala de espera
 * @author salerno 
 */
public class DezenhoServer {

	private static DezenhoServer instance = new DezenhoServer();
	private SalaEspera salaEspera;
	private List<Jogador> jogadoresConectados;
	private List<Partida> partidas;
	private Integer indicePartidaAtual;

	/**
	 * Construtor
	 */
	private DezenhoServer() {
		setSalaEspera(new SalaEspera());
		setJogadoresConectados(new ArrayList<Jogador>());
		setPartidas(new ArrayList<Partida>());
		setIndicePartidaAtual(0);
	}

	/**
	 * Responsavel chamar o metodo que inicia o servidor quando o servidor e
	 * levantado
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		getInstance().iniciaServidor();
		System.out.println("Pressione \"s\" para sair");
		Scanner leitura = new Scanner(System.in);
		while(!leitura.next().equals("s"));
		getInstance().encerraServidor();	
	}

	/**
	 * Metodo responsavel por Iniciar o servidor Sendo alocado no registro RMI
	 */
	public void iniciaServidor() {
		try {
			System.setSecurityManager(new RMISecurityManager());
			Registry r = LocateRegistry.getRegistry();
			r.bind("dezenhoServer", new ServerFacade());
			System.out.println("Servidor levantado!");
		} catch (Exception e) {
			System.out.println("Erro no levantamento do Servidor");
			e.printStackTrace();
		}
	}
	
	/**
	 * Metodo responsavel por encerrar o servidor desconectando os jogadores
	 */
	public void encerraServidor(){
		for(Jogador jogador : getJogadoresConectados()){
			desconectaUsuario(jogador.getIPUsuario());
		}
		try {
			Registry r = LocateRegistry.getRegistry();
			r.unbind("dezenhoServer");
			this.finalize();
			System.exit(0);
		}catch (Throwable e) {
			System.out.println("Erro no encerramento do servidor");
			e.printStackTrace();
		}
	}

	/**
	 * Conecta usuario
	 * 
	 * @param IPUsuario
	 * @param nome
	 */
	public void conectaUsuario(String IPUsuario, String nome) {

		try {
			ClientInterface client = (ClientInterface) Naming.lookup("rmi://"
					+ IPUsuario + "/dezenhoClient");
			Jogador jogadorConectado = new Jogador(client, nome, IPUsuario);
			adicionaJogadorConectado(jogadorConectado);
			getSalaEspera().insereJogadorSalaEspera(jogadorConectado);
		} catch (Exception e) {
			System.out.println("Erro na conexao do cliente");
			e.printStackTrace();
		}

	}

	/**
	 * Desconecta jogador cujo Ip � passado como parametro
	 * 
	 * @param IPUsuario
	 */
	public void desconectaUsuario(String IPUsuario) {
		for (Jogador jogador : getJogadoresConectados()) {
			if (jogador.getIPUsuario().equals(IPUsuario)) {
				removeJogadorConectado(jogador);
				if (jogador.getIdPartida() == -1) {
					getSalaEspera().removeJogadorSalaEspera(jogador);
				} else {
					getPartida(jogador.getIdPartida()).encerraPartida(jogador);
				}
				break;
			}
		}

	}

	public void insereJogadorSalaEspera(String IPusuario) {

		for (Jogador jogador : getJogadoresConectados()) {
			if (jogador.getIPUsuario().equals(IPusuario)) {
				getSalaEspera().insereJogadorSalaEspera(jogador);
				break;
			}
		}

	}

	/**
	 * Gera o proximo indice possivel para idParitida
	 * 
	 * @return proximo valor de idPartida
	 */
	public Integer getNextIndicePartida() {
		setIndicePartidaAtual(getIndicePartidaAtual() + 1);
		return getIndicePartidaAtual();
	}

	// Metodos de acesso

	protected SalaEspera getSalaEspera() {
		return salaEspera;
	}

	protected void setSalaEspera(SalaEspera salaEspera) {
		this.salaEspera = salaEspera;
	}

	public static DezenhoServer getInstance() {
		return instance;
	}

	protected List<Jogador> getJogadoresConectados() {
		return jogadoresConectados;
	}

	protected void setJogadoresConectados(List<Jogador> jogadoresConectados) {
		this.jogadoresConectados = jogadoresConectados;
	}

	protected List<Partida> getPartidas() {
		return partidas;
	}

	protected void setPartidas(List<Partida> partidas) {
		this.partidas = partidas;
	}

	/**
	 * Retorna a partida de acordo com o id Note que este id nao pode ser o
	 * indice da lista, uma vez que este numero e mudado a medida que as
	 * partidas sao criadas ou removidas
	 * 
	 * @param idPartida
	 */
	public Partida getPartida(Integer idPartida) {
		for (Partida partida : getPartidas())
			if (partida.getIdPartida().equals(idPartida))
				return partida;
		return null;
	}

	public void adicionaPartida(Partida partida) {
		getPartidas().add(partida);

	}

	public void removePartida(Partida partida) {
		getPartidas().remove(partida);

	}

	protected Integer getIndicePartidaAtual() {
		return indicePartidaAtual;
	}

	protected void setIndicePartidaAtual(Integer indicePartidaAtual) {
		this.indicePartidaAtual = indicePartidaAtual;
	}

	protected void adicionaJogadorConectado(Jogador jogadorConectado) {
		getJogadoresConectados().add(jogadorConectado);
	}

	protected void removeJogadorConectado(Jogador jogador) {
		getJogadoresConectados().remove(jogador);
	}

}